<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Spring Security  上 | FATE</title><meta name="keywords" content="SpringBoot,SpringSecurity"><meta name="author" content="MoYu-zc"><meta name="copyright" content="MoYu-zc"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="description" content="Spring Security  上Security-dome1.创建项目创建一个Spring Boot项目，不用加入什么依赖 2.导入依赖&lt;dependencies&gt;    &lt;!--启动器变为 web --&gt;    &lt;dependency&gt;        &lt;groupId&gt;org.springframework.boot&lt;&#x2F;groupId&amp;g">
<meta property="og:type" content="article">
<meta property="og:title" content="Spring Security  上">
<meta property="og:url" content="http://example.com/2021/05/04/Spring%20Security%20%20%E4%B8%8A/index.html">
<meta property="og:site_name" content="FATE">
<meta property="og:description" content="Spring Security  上Security-dome1.创建项目创建一个Spring Boot项目，不用加入什么依赖 2.导入依赖&lt;dependencies&gt;    &lt;!--启动器变为 web --&gt;    &lt;dependency&gt;        &lt;groupId&gt;org.springframework.boot&lt;&#x2F;groupId&amp;g">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210504135113.png">
<meta property="article:published_time" content="2021-05-03T16:00:00.000Z">
<meta property="article:modified_time" content="2021-06-13T14:29:17.779Z">
<meta property="article:author" content="MoYu-zc">
<meta property="article:tag" content="SpringBoot">
<meta property="article:tag" content="SpringSecurity">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210504135113.png"><link rel="shortcut icon" href="/img/favicon.png"><link rel="canonical" href="http://example.com/2021/05/04/Spring%20Security%20%20%E4%B8%8A/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css"><script>var GLOBAL_CONFIG = { 
  root: '/',
  hexoversion: '5.2.0',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  ClickShowText: undefined,
  lightbox: 'fancybox',
  Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#121212","position":"top-right"},
  justifiedGallery: {
    js: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js',
    css: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css'
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isanchor: false
};

var saveToLocal = {
  set: function setWithExpiry(key, value, ttl) {
    const now = new Date()
    const expiryDay = ttl * 86400000
    const item = {
      value: value,
      expiry: now.getTime() + expiryDay,
    }
    localStorage.setItem(key, JSON.stringify(item))
  },

  get: function getWithExpiry(key) {
    const itemStr = localStorage.getItem(key)

    if (!itemStr) {
      return undefined
    }
    const item = JSON.parse(itemStr)
    const now = new Date()

    if (now.getTime() > item.expiry) {
      localStorage.removeItem(key)
      return undefined
    }
    return item.value
  }
}</script><script id="config_change">var GLOBAL_CONFIG_SITE = { 
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isSidebar: true,
  postUpdate: '2021-06-13 22:29:17'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(function () {
  window.activateDarkMode = function () {
    document.documentElement.setAttribute('data-theme', 'dark')
    if (document.querySelector('meta[name="theme-color"]') !== null) {
      document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
    }
  }
  window.activateLightMode = function () {
    document.documentElement.setAttribute('data-theme', 'light')
    if (document.querySelector('meta[name="theme-color"]') !== null) {
      document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
    }
  }

  const autoChangeMode = 'false'
  const t = saveToLocal.get('theme')
  if (autoChangeMode === '1') {
    const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches
    const isLightMode = window.matchMedia('(prefers-color-scheme: light)').matches
    const isNotSpecified = window.matchMedia('(prefers-color-scheme: no-preference)').matches
    const hasNoSupport = !isDarkMode && !isLightMode && !isNotSpecified

    if (t === undefined) {
      if (isLightMode) activateLightMode()
      else if (isDarkMode) activateDarkMode()
      else if (isNotSpecified || hasNoSupport) {
        const now = new Date()
        const hour = now.getHours()
        const isNight = hour <= 6 || hour >= 18
        isNight ? activateDarkMode() : activateLightMode()
      }
      window.matchMedia('(prefers-color-scheme: dark)').addListener(function (e) {
        if (saveToLocal.get('theme') === undefined) {
          e.matches ? activateDarkMode() : activateLightMode()
        }
      })
    } else if (t === 'light') activateLightMode()
    else activateDarkMode()
  } else if (autoChangeMode === '2') {
    const now = new Date()
    const hour = now.getHours()
    const isNight = hour <= 6 || hour >= 18
    if (t === undefined) isNight ? activateDarkMode() : activateLightMode()
    else if (t === 'light') activateLightMode()
    else activateDarkMode()
  } else {
    if (t === 'dark') activateDarkMode()
    else if (t === 'light') activateLightMode()
  }
})()</script><link rel="stylesheet" href="/css/iconfont.css"><link rel="stylesheet" href="/css/mycss.css"><meta name="generator" content="Hexo 5.2.0"><link rel="alternate" href="/atom.xml" title="FATE" type="application/atom+xml">
</head><body><div id="mobile-sidebar"><div id="menu_mask"></div><div id="mobile-sidebar-menus"><div class="mobile_author_icon"><img class="avatar-img" data-lazy-src="/img/tit.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="mobile_post_data"><div class="mobile_data_item is-center"><div class="mobile_data_link"><a href="/archives/"><div class="headline">文章</div><div class="length_num">39</div></a></div></div><div class="mobile_data_item is-center">      <div class="mobile_data_link"><a href="/tags/"><div class="headline">标签</div><div class="length_num">40</div></a></div></div><div class="mobile_data_item is-center">     <div class="mobile_data_link"><a href="/categories/"><div class="headline">分类</div><div class="length_num">22</div></a></div></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fa fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fa fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fa fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/messageboard/"><i class="fa-fw fa fa-paper-plane"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/timeline/"><i class="fa-fw fa fa-bell"></i><span> 日志</span></a></div><div class="menus_item"><a class="site-page" href="/myself/"><i class="fa-fw fa fa-address-card"></i><span> myself</span></a></div><div class="menus_item"><a class="site-page" target="_blank" rel="noopener" href="https://www.moyuzc.cn/"><i class="fa-fw fa fa-home"></i><span> Vuepress</span></a></div></div></div></div><div id="body-wrap"><div id="web_bg"></div><div id="sidebar"><i class="fas fa-arrow-right on" id="toggle-sidebar"></i><div class="sidebar-toc"><div class="sidebar-toc__title">目录</div><div class="sidebar-toc__progress"><span class="progress-notice">你已经读了</span><span class="progress-num">0</span><span class="progress-percentage">%</span><div class="sidebar-toc__progress-bar">     </div></div><div class="sidebar-toc__content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Spring-Security-%E4%B8%8A"><span class="toc-text">Spring Security  上</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#Security-dome"><span class="toc-text">Security-dome</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE"><span class="toc-text">1.创建项目</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E5%AF%BC%E5%85%A5%E4%BE%9D%E8%B5%96"><span class="toc-text">2.导入依赖</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-%E5%88%9B%E5%BB%BA%E6%8E%A7%E5%88%B6%E5%B1%82"><span class="toc-text">3.创建控制层</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%BF%AE%E6%94%B9%E7%AB%AF%E5%8F%A3%E5%8F%B7"><span class="toc-text">4.配置文件修改端口号</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-%E8%BF%90%E8%A1%8C%E6%B5%8B%E8%AF%95"><span class="toc-text">5.运行测试</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-%E7%99%BB%E5%BD%95"><span class="toc-text">6.登录</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Security-%E5%8E%9F%E7%90%86"><span class="toc-text">Security 原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%87%E6%BB%A4%E5%99%A8%E5%8A%A0%E8%BD%BD%E6%AD%A5%E9%AA%A4"><span class="toc-text">过滤器加载步骤</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%AD%A5%E9%AA%A4%E6%B5%81%E7%A8%8B"><span class="toc-text">步骤流程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%80%BB%E7%BB%93%EF%BC%9A"><span class="toc-text">总结：</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#UserDetailsService-%E6%8E%A5%E5%8F%A3"><span class="toc-text">UserDetailsService 接口</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%AD%A5%E9%AA%A4%EF%BC%9A"><span class="toc-text">步骤：</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#PasswordEncoder%E6%8E%A5%E5%8F%A3"><span class="toc-text">PasswordEncoder接口</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8A%A0%E5%AF%86%E6%96%B9%E6%B3%95%EF%BC%9A"><span class="toc-text">加密方法：</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Web%E6%9D%83%E9%99%90"><span class="toc-text">Web权限</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-%E9%80%9A%E8%BF%87%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6"><span class="toc-text">1.通过配置文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E9%80%9A%E8%BF%87%E9%85%8D%E7%BD%AE%E7%B1%BB"><span class="toc-text">2.通过配置类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BC%96%E5%86%99%E5%AE%9E%E7%8E%B0%E7%B1%BB"><span class="toc-text">3.自定义编写实现类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-%E8%BF%9E%E6%8E%A5%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%8C%E6%88%90%E7%94%A8%E6%88%B7%E8%AE%A4%E8%AF%81"><span class="toc-text">4.连接数据库完成用户认证</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E6%95%B0%E6%8D%AE%E5%BA%93"><span class="toc-text">创建数据库</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BC%95%E5%85%A5%E4%BE%9D%E8%B5%96"><span class="toc-text">引入依赖</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%85%8D%E7%BD%AEJDBC%E4%BF%A1%E6%81%AF"><span class="toc-text">配置JDBC信息</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E5%AE%9E%E4%BD%93%E7%B1%BB%E3%80%81Mapper%E6%8E%A5%E5%8F%A3"><span class="toc-text">创建实体类、Mapper接口</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9B%E5%BB%BAUserDetailsService%E7%B1%BB"><span class="toc-text">创建UserDetailsService类</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-%E8%87%AA%E5%AE%9A%E4%B9%89%E7%99%BB%E5%BD%95%E9%A1%B5%E9%9D%A2"><span class="toc-text">5.自定义登录页面</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9F%BA%E4%BA%8E%E8%A7%92%E8%89%B2%E6%88%96%E6%9D%83%E9%99%90%E7%9A%84%E8%AE%BF%E9%97%AE%E6%8E%A7%E5%88%B6"><span class="toc-text">基于角色或权限的访问控制</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-hasAuthority%E6%96%B9%E6%B3%95"><span class="toc-text">1.hasAuthority方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-hasAnyAuthority%E6%96%B9%E6%B3%95"><span class="toc-text">2.hasAnyAuthority方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-hasRole%E6%96%B9%E6%B3%95"><span class="toc-text">3.hasRole方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-hasAnyRole%E6%96%B9%E6%B3%95"><span class="toc-text">4.hasAnyRole方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-%E8%87%AA%E5%AE%9A%E4%B9%89403%E9%A1%B5%E9%9D%A2"><span class="toc-text">5.自定义403页面</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#1-%E5%88%9B%E5%BB%BA%E8%87%AA%E5%AE%9A%E4%B9%89403%E9%A1%B5%E9%9D%A2"><span class="toc-text">1.创建自定义403页面</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-%E4%BF%AE%E6%94%B9%E9%85%8D%E7%BD%AE%E7%B1%BB"><span class="toc-text">2.修改配置类</span></a></li></ol></li></ol></li></ol></li></ol></div></div></div><header class="post-bg" id="page-header" style="background-image: url(https://gitee.com/MoYu-zc/picgo/raw/master/img/20210504135113.png)"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">FATE</a></span><span id="menus"><div id="search_button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fa fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fa fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fa fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/messageboard/"><i class="fa-fw fa fa-paper-plane"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/timeline/"><i class="fa-fw fa fa-bell"></i><span> 日志</span></a></div><div class="menus_item"><a class="site-page" href="/myself/"><i class="fa-fw fa fa-address-card"></i><span> myself</span></a></div><div class="menus_item"><a class="site-page" target="_blank" rel="noopener" href="https://www.moyuzc.cn/"><i class="fa-fw fa fa-home"></i><span> Vuepress</span></a></div></div><span class="close" id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></span></span></nav><div id="post-info"><div id="post-title"><div class="posttitle">Spring Security  上</div></div><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-05-03T16:00:00.000Z" title="发表于 2021-05-04 00:00:00">2021-05-04</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-06-13T14:29:17.779Z" title="更新于 2021-06-13 22:29:17">2021-06-13</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/SpringSecurity/">SpringSecurity</a></span></div><div class="meta-secondline"> <span class="post-meta-separator">|</span><span class="post-meta-pv-cv"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"></span></span></div></div></div></header><main class="layout_post" id="content-inner"><article id="post"><div class="post-content" id="article-container"><h1 id="Spring-Security-上"><a href="#Spring-Security-上" class="headerlink" title="Spring Security  上"></a>Spring Security  上</h1><h2 id="Security-dome"><a href="#Security-dome" class="headerlink" title="Security-dome"></a>Security-dome</h2><h3 id="1-创建项目"><a href="#1-创建项目" class="headerlink" title="1.创建项目"></a>1.创建项目</h3><p><strong>创建一个Spring Boot项目，不用加入什么依赖</strong></p>
<h3 id="2-导入依赖"><a href="#2-导入依赖" class="headerlink" title="2.导入依赖"></a>2.导入依赖</h3><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--启动器变为 web --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--security启动器 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-security<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="3-创建控制层"><a href="#3-创建控制层" class="headerlink" title="3.创建控制层"></a>3.创建控制层</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestController</span> </span>&#123;</span><br><span class="line">    <span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">hello</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;hello Security&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="4-配置文件修改端口号"><a href="#4-配置文件修改端口号" class="headerlink" title="4.配置文件修改端口号"></a>4.配置文件修改端口号</h3><figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="meta">server.port</span>=<span class="string">8081</span></span><br></pre></td></tr></table></figure>

<h3 id="5-运行测试"><a href="#5-运行测试" class="headerlink" title="5.运行测试"></a>5.运行测试</h3><p>运行网址为：</p>
<p><a target="_blank" rel="noopener" href="http://localhost:8081/hello">http://localhost:8081/hello</a></p>
<p>这时候会发现，网址会自动变为：</p>
<p><a target="_blank" rel="noopener" href="http://localhost:8081/login">http://localhost:8081/login</a> </p>
<p><img src= "/img/loading.gif" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210412222952.png" alt="1"></p>
<h3 id="6-登录"><a href="#6-登录" class="headerlink" title="6.登录"></a>6.登录</h3><p>能看到，在该页面中有<code>账号</code>和<code>密码</code></p>
<p>默认账号：<strong>user</strong></p>
<p>默认密码：</p>
<p><img src= "/img/loading.gif" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210412225302.png" alt="2"></p>
<p><strong>登录之后：</strong></p>
<p><img src= "/img/loading.gif" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210412225406.png" alt="3"></p>
<h2 id="Security-原理"><a href="#Security-原理" class="headerlink" title="Security 原理"></a>Security 原理</h2><blockquote>
<p><strong>Spring Security</strong> 本质是一个过滤器链</p>
</blockquote>
<p><strong>FilterSecurityInterceptor</strong>：是一个方法级的 <code>权限过滤器</code> ，基本位于过滤链的最底部</p>
<hr>
<p><strong>ExceptionTranslationFilter</strong>：是个<code>异常过滤器</code>，用来处理在认证授权过程中抛出的异常</p>
<hr>
<p><strong>UsernamePasswordAuthenticationFilter</strong>：对 <code>/login</code> 的POST请求做拦截，校验表单中用户名，密码</p>
<hr>
<h3 id="过滤器加载步骤"><a href="#过滤器加载步骤" class="headerlink" title="过滤器加载步骤"></a>过滤器加载步骤</h3><h4 id="步骤流程"><a href="#步骤流程" class="headerlink" title="步骤流程"></a>步骤流程</h4><p>使用Spring Security配置过滤器  :  <strong>DelegatingFilterProxy</strong></p>
<p>源代码如下</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doFilter</span><span class="params">(ServletRequest request, ServletResponse response, FilterChain filterChain)</span> <span class="keyword">throws</span> ServletException, IOException </span>&#123;</span><br><span class="line">    Filter delegateToUse = <span class="keyword">this</span>.delegate;</span><br><span class="line">    <span class="keyword">if</span> (delegateToUse == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">synchronized</span>(<span class="keyword">this</span>.delegateMonitor) &#123;</span><br><span class="line">            delegateToUse = <span class="keyword">this</span>.delegate;</span><br><span class="line">            <span class="keyword">if</span> (delegateToUse == <span class="keyword">null</span>) &#123;</span><br><span class="line">                WebApplicationContext wac = <span class="keyword">this</span>.findWebApplicationContext();</span><br><span class="line">                <span class="keyword">if</span> (wac == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">&quot;No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                delegateToUse = <span class="keyword">this</span>.initDelegate(wac);</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">this</span>.delegate = delegateToUse;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">this</span>.invokeDelegate(delegateToUse, request, response, filterChain);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>即为：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doFilter</span><span class="params">(ServletRequest request, ServletResponse response, FilterChain filterChain)</span> <span class="keyword">throws</span> ServletException, IOException </span>&#123;</span><br><span class="line">        <span class="comment">//进行判断</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">//初始化</span></span><br><span class="line">		delegateToUse = <span class="keyword">this</span>.initDelegate(wac);</span><br><span class="line">		</span><br><span class="line">    	<span class="comment">//其余部分</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后我们查看 <strong>initDelegate</strong>：</p>
<p><img src= "/img/loading.gif" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210413153343.png" alt="4"></p>
<p>初始化为  <strong>FilterChainProxy 对象</strong></p>
<p>进入 <strong>FilterChainProxy：</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doFilter</span><span class="params">(ServletRequest request, ServletResponse response, FilterChain chain)</span> <span class="keyword">throws</span> IOException, ServletException </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> clearContext = request.getAttribute(FILTER_APPLIED) == <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">if</span> (!clearContext) &#123;</span><br><span class="line">        <span class="comment">//满足条件  运行该方法</span></span><br><span class="line">        <span class="keyword">this</span>.doFilterInternal(request, response, chain);</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            request.setAttribute(FILTER_APPLIED, Boolean.TRUE);</span><br><span class="line">            <span class="comment">//不满足  最终还是需要运行该方法</span></span><br><span class="line">            <span class="keyword">this</span>.doFilterInternal(request, response, chain);</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (RequestRejectedException var9) &#123;</span><br><span class="line">            <span class="keyword">this</span>.requestRejectedHandler.handle((HttpServletRequest)request, (HttpServletResponse)response, var9);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            SecurityContextHolder.clearContext();</span><br><span class="line">            request.removeAttribute(FILTER_APPLIED);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>可以看出，无论满不满足条件，最终都需要运行 doFilterInternal()方法</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">doFilterInternal</span><span class="params">(ServletRequest request, ServletResponse response, FilterChain chain)</span> <span class="keyword">throws</span> IOException, ServletException </span>&#123;</span><br><span class="line">     <span class="comment">/*</span></span><br><span class="line"><span class="comment">    *  部分代码。。。</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    List&lt;Filter&gt; filters = <span class="keyword">this</span>.getFilters((HttpServletRequest)firewallRequest);</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    *  部分代码。。。</span></span><br><span class="line"><span class="comment">    */</span></span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> List&lt;Filter&gt; <span class="title">getFilters</span><span class="params">(HttpServletRequest request)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line">    Iterator var3 = <span class="keyword">this</span>.filterChains.iterator();</span><br><span class="line">    SecurityFilterChain chain;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!var3.hasNext()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        chain = (SecurityFilterChain)var3.next();</span><br><span class="line">        <span class="keyword">if</span> (logger.isTraceEnabled()) &#123;</span><br><span class="line">            ++count;</span><br><span class="line">            logger.trace(LogMessage.format(<span class="string">&quot;Trying to match request against %s (%d/%d)&quot;</span>, chain, count, <span class="keyword">this</span>.filterChains.size()));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">while</span>(!chain.matches(request));</span><br><span class="line">     <span class="comment">//返回所有过滤器</span></span><br><span class="line">    <span class="keyword">return</span> chain.getFilters();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>所以 doFilterInternal() 方法 可以返回 <strong>所有要进行加载的过滤器</strong></p>
<hr>
<h4 id="总结："><a href="#总结：" class="headerlink" title="总结："></a><strong>总结：</strong></h4><ol>
<li>配置过滤器 <strong>DelegatingFilterProxy</strong></li>
<li>在其中进行初始化  <strong>initDelegate</strong> </li>
<li>在初始化中得到  <strong>FilterChainProxy 对象</strong></li>
<li>在其中运行的就是 <strong>doFilterInternal()</strong> 方法，该方法返回的就是 <strong>所有要进行加载的过滤器</strong></li>
</ol>
<h3 id="UserDetailsService-接口"><a href="#UserDetailsService-接口" class="headerlink" title="UserDetailsService 接口"></a>UserDetailsService 接口</h3><blockquote>
<p> <strong>UserDetailsService接口 ： 查询数据库用户名和密码过程</strong></p>
</blockquote>
<h4 id="步骤："><a href="#步骤：" class="headerlink" title="步骤："></a>步骤：</h4><ol>
<li>创建类继承<strong>UsernamePasswordAuthenticationFilter</strong>，重写三个方法： attemptAuthentication() 、successfulAuthentication()、unsuccessfulAuthentication()</li>
<li>如果成功调用successfulAuthentication()，反之调用unsuccessfulAuthentication()</li>
<li>创建类实现UserDetailService，编写查询数据过程，返回<strong>User</strong>对象，这个User对象是安全框架提供对象</li>
</ol>
<h3 id="PasswordEncoder接口"><a href="#PasswordEncoder接口" class="headerlink" title="PasswordEncoder接口"></a>PasswordEncoder接口</h3><blockquote>
<p> <strong>PasswordEncoder接口 : 数据加密接口，用于返回User对象里面密码加密</strong></p>
</blockquote>
<h4 id="加密方法："><a href="#加密方法：" class="headerlink" title="加密方法："></a>加密方法：</h4><blockquote>
<p><strong>BCryptPasswordEncoder</strong>是Spring Security官方推荐的密码解析器，平时多使用这个解析器。<br><strong>BCryptPasswordEncoder</strong>是对bcrypt强散列方法的具体实现。是基于Hash算法实现的单向加密。可以通过strength控制加密强度，默认10.</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">BCryptPasswordEncoder b = <span class="keyword">new</span> BCryptPasswordEncoder();</span><br><span class="line">String zc = b.encode(<span class="string">&quot;zc&quot;</span>);   <span class="comment">//加密成功</span></span><br></pre></td></tr></table></figure>



<h2 id="Web权限"><a href="#Web权限" class="headerlink" title="Web权限"></a>Web权限</h2><p>在 <code>Security-dome</code>  中可以看到，如果想要进入页面，还需要输入账号密码</p>
<p>而对于登陆时候的账号密码可以进行<strong>自定义设置</strong>：</p>
<ol>
<li>通过配置文件</li>
<li>通过配置类</li>
<li>自定义编写实现类</li>
</ol>
<h3 id="1-通过配置文件"><a href="#1-通过配置文件" class="headerlink" title="1.通过配置文件"></a>1.通过配置文件</h3><figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="meta">spring.security.user.name</span>=<span class="string">root</span></span><br><span class="line"><span class="meta">spring.security.user.password</span>=<span class="string">root</span></span><br></pre></td></tr></table></figure>

<p>这个时候再运行，会发现控制台不会出现<code>密码</code>，可以直接通过设置的账号密码登录</p>
<h3 id="2-通过配置类"><a href="#2-通过配置类" class="headerlink" title="2.通过配置类"></a>2.通过配置类</h3><ol>
<li>创建一个 SecurityConfig 配置类</li>
<li>重写configure()方法，注意看清参数，不要选错方法</li>
<li>很重要的一点：需要注入PasswordEncoder接口</li>
</ol>
<blockquote>
<p>如果不注入该接口，可能报 <strong>Encoded password does not look like BCrypt</strong></p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SecurityConfig</span> <span class="keyword">extends</span> <span class="title">WebSecurityConfigurerAdapter</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(AuthenticationManagerBuilder auth)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        BCryptPasswordEncoder bCryptPasswordEncoder = <span class="keyword">new</span> BCryptPasswordEncoder();</span><br><span class="line">        String password = bCryptPasswordEncoder.encode(<span class="string">&quot;root&quot;</span>);</span><br><span class="line">        auth.inMemoryAuthentication()</span><br><span class="line">                .withUser(<span class="string">&quot;root&quot;</span>)  		<span class="comment">//账号</span></span><br><span class="line">                .password(password)     <span class="comment">//加密的密码</span></span><br><span class="line">                .roles(<span class="string">&quot;admin&quot;</span>);        <span class="comment">//权限</span></span><br><span class="line">    &#125;</span><br><span class="line">   </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function">PasswordEncoder <span class="title">passwordEncoder</span><span class="params">()</span></span>&#123;</span><br><span class="line">       <span class="keyword">return</span> <span class="keyword">new</span> BCryptPasswordEncoder();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这时候，也可以直接使用你设置的账号密码登录页面</p>
<h3 id="3-自定义编写实现类"><a href="#3-自定义编写实现类" class="headerlink" title="3.自定义编写实现类"></a>3.自定义编写实现类</h3><ol>
<li>编写userDetailsService实现类，返回User对象</li>
<li>创建一个 SecurityConfig 配置类</li>
</ol>
<p><strong>编写一个UserDetailsService实现类</strong></p>
<p>在其中需要重写  loadUserByUsername()  方法，该方法用于登录</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Service(&quot;userDetailsService&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyuserDetailsService</span> <span class="keyword">implements</span> <span class="title">UserDetailsService</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> UserDetails <span class="title">loadUserByUsername</span><span class="params">(String s)</span> <span class="keyword">throws</span> UsernameNotFoundException </span>&#123;</span><br><span class="line">        List&lt;GrantedAuthority&gt; auths =</span><br><span class="line">                AuthorityUtils.commaSeparatedStringToAuthorityList(<span class="string">&quot;role&quot;</span>);</span><br><span class="line">        <span class="comment">//返回的实际上是一个User对象，参数解析可以看下面</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> User(<span class="string">&quot;root&quot;</span>,</span><br><span class="line">                <span class="keyword">new</span> BCryptPasswordEncoder().encode(<span class="string">&quot;root&quot;</span>),auths);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p><strong>UserDetailsService</strong> 解析</p>
</blockquote>
<p>对于该实现类中重写的  loadUserByUsername()  方法，返回的是 <strong>UserDetails</strong> 接口</p>
<p><img src= "/img/loading.gif" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210413172403.png" alt="5"></p>
<p>在源代码中可以看出，实际上 <strong>UserDetails</strong> 接口，返回的是一个 <strong>User</strong> 对象</p>
<p><img src= "/img/loading.gif" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210413172511.png" alt="6"></p>
<p>而在User对象中，需要返回三个参数：</p>
<p>String、String、Collection；</p>
<p>账号   、 密码  、集合(权限等信息)</p>
<hr>
<p><strong>创建一个 SecurityConfig 配置类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SecurityConfigTest</span> <span class="keyword">extends</span> <span class="title">WebSecurityConfigurerAdapter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    UserDetailsService userDetailsService;  <span class="comment">// 这里应和 @Service(&quot;userDetailsService&quot;) 中内容相同</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(AuthenticationManagerBuilder auth)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="comment">//使用该方法</span></span><br><span class="line">        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function">PasswordEncoder <span class="title">passwordEncoder</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> BCryptPasswordEncoder();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这时候测试，也可以直接使用设置的账号密码登录</p>
<p>之后，如果连接数据库，一般都是用<strong>第三种方式</strong></p>
<h3 id="4-连接数据库完成用户认证"><a href="#4-连接数据库完成用户认证" class="headerlink" title="4.连接数据库完成用户认证"></a>4.连接数据库完成用户认证</h3><p><strong>(该方法是在第三种方法代码基础上完成)</strong></p>
<ol>
<li>创建数据库</li>
<li>整合Mybatis-Plus完成数据库操作</li>
<li>配置JDBC信息</li>
<li>创建实体类、Mapper接口</li>
<li>创建UserDetailsService类</li>
</ol>
<h4 id="创建数据库"><a href="#创建数据库" class="headerlink" title="创建数据库"></a>创建数据库</h4><p>创建了一个 <code>mybatis-plus</code> 数据库 ，其中创建了一个<code>users</code>表，记得创建后，加入数据</p>
<p><img src= "/img/loading.gif" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210413224942.png" alt="7"></p>
<h4 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</h4><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Mybatis-plus--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.baomidou<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-plus-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.4.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- mysql--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- Lombok--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="配置JDBC信息"><a href="#配置JDBC信息" class="headerlink" title="配置JDBC信息"></a>配置JDBC信息</h4><figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="meta">spring.datasource.driver-class-name</span>=<span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line"><span class="meta">spring.datasource.url</span>=<span class="string">jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=UTC</span></span><br><span class="line"><span class="meta">spring.datasource.username</span>=<span class="string">root</span></span><br><span class="line"><span class="meta">spring.datasource.password</span>=<span class="string">root</span></span><br></pre></td></tr></table></figure>

<h4 id="创建实体类、Mapper接口"><a href="#创建实体类、Mapper接口" class="headerlink" title="创建实体类、Mapper接口"></a>创建实体类、Mapper接口</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Data</span>    <span class="comment">// 引入了Lombok才可以使用</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Users</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UsersMapper</span> <span class="keyword">extends</span> <span class="title">BaseMapper</span>&lt;<span class="title">Users</span>&gt; </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="创建UserDetailsService类"><a href="#创建UserDetailsService类" class="headerlink" title="创建UserDetailsService类"></a>创建UserDetailsService类</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Service(&quot;userDetailsService&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyuserDetailsService</span> <span class="keyword">implements</span> <span class="title">UserDetailsService</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UsersMapper usersMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> UserDetails <span class="title">loadUserByUsername</span><span class="params">(String username)</span> <span class="keyword">throws</span> UsernameNotFoundException </span>&#123;</span><br><span class="line">        <span class="comment">//调用usersMapper方法</span></span><br><span class="line">        QueryWrapper&lt;Users&gt; wrapper = <span class="keyword">new</span> QueryWrapper&lt;&gt;();</span><br><span class="line">        wrapper.eq(<span class="string">&quot;username&quot;</span>,username);</span><br><span class="line">        Users users = usersMapper.selectOne(wrapper);</span><br><span class="line">        <span class="keyword">if</span> (users == <span class="keyword">null</span>)&#123;</span><br><span class="line">            <span class="comment">//数据库没有用户名，认证失败</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> UsernameNotFoundException(<span class="string">&quot;用户名不存在&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        List&lt;GrantedAuthority&gt; auths =</span><br><span class="line">                AuthorityUtils.commaSeparatedStringToAuthorityList(<span class="string">&quot;role&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> User(users.getUsername(),</span><br><span class="line">                <span class="keyword">new</span> BCryptPasswordEncoder().encode(users.getPassword()),auths);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>这时候就可以正常运行了</strong></p>
<h3 id="5-自定义登录页面"><a href="#5-自定义登录页面" class="headerlink" title="5.自定义登录页面"></a>5.自定义登录页面</h3><p><strong>在上面代码的基础上完成该部分代码</strong></p>
<p><strong>1.创建前端页面</strong></p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Title<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span> <span class="attr">action</span>=<span class="string">&quot;/user/login&quot;</span>&gt;</span></span><br><span class="line">        用户名：<span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">        密码：<span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;login&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p><strong>2.书写Controller层代码</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/index&quot;)</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">index</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;index&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>3.在创建的配置类中重写 <strong>configure(HttpSecurity http)</strong> 方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    http.formLogin()   <span class="comment">//自定义自己编写的登录页面</span></span><br><span class="line">        .loginPage(<span class="string">&quot;/login.html&quot;</span>)  <span class="comment">//设置登陆页面</span></span><br><span class="line">        .loginProcessingUrl(<span class="string">&quot;/login&quot;</span>) <span class="comment">//成功登录访问路径 , 该处路径和from表单中的action路径统一</span></span><br><span class="line">        .defaultSuccessUrl(<span class="string">&quot;/index&quot;</span>).permitAll()  <span class="comment">//登录成功之后跳转路径</span></span><br><span class="line">        .and().authorizeRequests()</span><br><span class="line">        .antMatchers(<span class="string">&quot;/&quot;</span>,<span class="string">&quot;/hello&quot;</span>,<span class="string">&quot;/login&quot;</span>) <span class="comment">//可以直接访问的路径，不需要认证</span></span><br><span class="line">        .permitAll()</span><br><span class="line">        .anyRequest().authenticated()</span><br><span class="line">        .and().csrf().disable(); <span class="comment">//关闭csrf</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这时候可以分别测试进入以下两个路径：</p>
<p><a target="_blank" rel="noopener" href="http://localhost:8081/hello">http://localhost:8081/hello</a></p>
<p><a target="_blank" rel="noopener" href="http://localhost:8081/index">http://localhost:8081/index</a></p>
<p>会发现，第一个 hello 路径 ，不会拦截了，可以直接进入页面</p>
<p>第二个index，会进入自定义的登陆页面，登陆成功后，才可以进入</p>
<h2 id="基于角色或权限的访问控制"><a href="#基于角色或权限的访问控制" class="headerlink" title="基于角色或权限的访问控制"></a>基于角色或权限的访问控制</h2><h3 id="1-hasAuthority方法"><a href="#1-hasAuthority方法" class="headerlink" title="1.hasAuthority方法"></a>1.hasAuthority方法</h3><blockquote>
<p>如果当前的主体具有指定的权限，则返回<strong>true</strong>，否则返回<strong>false</strong></p>
</blockquote>
<ol>
<li>修改配置类</li>
<li>在 UserDetailsService 实现类中添加权限</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    http.formLogin() </span><br><span class="line">        .loginPage(<span class="string">&quot;/login.html&quot;</span>) </span><br><span class="line">        .loginProcessingUrl(<span class="string">&quot;/user/login&quot;</span>) </span><br><span class="line">        .defaultSuccessUrl(<span class="string">&quot;/index&quot;</span>).permitAll()  </span><br><span class="line">        .and().authorizeRequests()</span><br><span class="line">        .antMatchers(<span class="string">&quot;/&quot;</span>,<span class="string">&quot;/hello&quot;</span>,<span class="string">&quot;/user/login&quot;</span>) </span><br><span class="line">        .permitAll()</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//当前登录用户，只有具有admins权限才可以访问这个路径</span></span><br><span class="line">        .antMatchers(<span class="string">&quot;/index&quot;</span>).hasAuthority(<span class="string">&quot;admins&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        .anyRequest().authenticated()</span><br><span class="line">        .and().csrf().disable(); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> UserDetails <span class="title">loadUserByUsername</span><span class="params">(String username)</span> <span class="keyword">throws</span> UsernameNotFoundException </span>&#123;</span><br><span class="line">    QueryWrapper&lt;Users&gt; wrapper = <span class="keyword">new</span> QueryWrapper&lt;&gt;();</span><br><span class="line">    wrapper.eq(<span class="string">&quot;username&quot;</span>,username);</span><br><span class="line">    Users users = usersMapper.selectOne(wrapper);</span><br><span class="line">    <span class="keyword">if</span> (users == <span class="keyword">null</span>)&#123; </span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> UsernameNotFoundException(<span class="string">&quot;用户名不存在&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    List&lt;GrantedAuthority&gt; auths =</span><br><span class="line">            AuthorityUtils.commaSeparatedStringToAuthorityList(<span class="string">&quot;admins&quot;</span>);    <span class="comment">//这里添加权限</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> User(users.getUsername(),</span><br><span class="line">            <span class="keyword">new</span> BCryptPasswordEncoder().encode(users.getPassword()),auths);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进行测试，路径为：</p>
<p><a target="_blank" rel="noopener" href="http://localhost:8081/index">http://localhost:8081/index</a></p>
<ul>
<li>如果权限不通过  ， <strong>403 无权限</strong></li>
</ul>
<p><img src= "/img/loading.gif" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210414143714.png" alt="8"></p>
<ul>
<li>如果权限通过 ，<strong>正常运行</strong></li>
</ul>
<h3 id="2-hasAnyAuthority方法"><a href="#2-hasAnyAuthority方法" class="headerlink" title="2.hasAnyAuthority方法"></a>2.hasAnyAuthority方法</h3><blockquote>
<p>如果当前的主体有任何提供的角色（给定的作为一个逗号分隔的字符串列表）的话，返回 <strong>true</strong></p>
<p>与 hasAuthority() 的区别是 </p>
<ul>
<li><p>hasAuthority() 参数唯一，只能满足这一个权限才可以</p>
</li>
<li><p>而该方法，参数可以多个，满足其中一个权限 即为通过</p>
</li>
</ul>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    http.formLogin() </span><br><span class="line">        .loginPage(<span class="string">&quot;/login.html&quot;</span>) </span><br><span class="line">        .loginProcessingUrl(<span class="string">&quot;/user/login&quot;</span>) </span><br><span class="line">        .defaultSuccessUrl(<span class="string">&quot;/index&quot;</span>).permitAll()  </span><br><span class="line">        .and().authorizeRequests()</span><br><span class="line">        .antMatchers(<span class="string">&quot;/&quot;</span>,<span class="string">&quot;/hello&quot;</span>,<span class="string">&quot;/user/login&quot;</span>) </span><br><span class="line">        .permitAll()</span><br><span class="line"></span><br><span class="line">        <span class="comment">//当前登录用户，具有admins或者user权限才可以访问这个路径</span></span><br><span class="line">        .antMatchers(<span class="string">&quot;/index&quot;</span>).hasAnyAuthority(<span class="string">&quot;admins&quot;</span>,<span class="string">&quot;user&quot;</span>)</span><br><span class="line"></span><br><span class="line">        .anyRequest().authenticated()</span><br><span class="line">        .and().csrf().disable(); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="3-hasRole方法"><a href="#3-hasRole方法" class="headerlink" title="3.hasRole方法"></a>3.hasRole方法</h3><blockquote>
<p>如果用户具备给定角色就 允许访问，否则出现 403<br>如果当前主体具有指定的角色，则返回 true</p>
<p>该方法与 hasAuthority 方法，使用方法基本相同，区别就是 他需要在权限前加上 <code>ROLE_</code></p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    http.formLogin() </span><br><span class="line">        .loginPage(<span class="string">&quot;/login.html&quot;</span>) </span><br><span class="line">        .loginProcessingUrl(<span class="string">&quot;/user/login&quot;</span>) </span><br><span class="line">        .defaultSuccessUrl(<span class="string">&quot;/index&quot;</span>).permitAll()  </span><br><span class="line">        .and().authorizeRequests()</span><br><span class="line">        .antMatchers(<span class="string">&quot;/&quot;</span>,<span class="string">&quot;/hello&quot;</span>,<span class="string">&quot;/user/login&quot;</span>) </span><br><span class="line">        .permitAll()</span><br><span class="line"></span><br><span class="line">        <span class="comment">//当前登录用户，只有具有 ROLE_user 权限才可以访问这个路径</span></span><br><span class="line">        .antMatchers(<span class="string">&quot;/index&quot;</span>).hasRole(<span class="string">&quot;user&quot;</span>)</span><br><span class="line"></span><br><span class="line">        .anyRequest().authenticated()</span><br><span class="line">        .and().csrf().disable(); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// UserDetailsService 实现类中添加权限</span></span><br><span class="line">List&lt;GrantedAuthority&gt; auths =</span><br><span class="line">                AuthorityUtils.commaSeparatedStringToAuthorityList(<span class="string">&quot;admins,ROLE_user&quot;</span>);</span><br></pre></td></tr></table></figure>

<p>这时候可以正常运行</p>
<h3 id="4-hasAnyRole方法"><a href="#4-hasAnyRole方法" class="headerlink" title="4.hasAnyRole方法"></a>4.hasAnyRole方法</h3><blockquote>
<p>表示用户具备任何一个条件都可以访问</p>
<p><strong>该方法与 hasRole() 的区别 与1 2 两种方法相同</strong>，大家可以自行测试</p>
</blockquote>
<h3 id="5-自定义403页面"><a href="#5-自定义403页面" class="headerlink" title="5.自定义403页面"></a>5.自定义403页面</h3><h4 id="1-创建自定义403页面"><a href="#1-创建自定义403页面" class="headerlink" title="1.创建自定义403页面"></a>1.创建自定义403页面</h4><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Title<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">h1</span>&gt;</span>没有权限访问！！！<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="2-修改配置类"><a href="#2-修改配置类" class="headerlink" title="2.修改配置类"></a>2.修改配置类</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    http.exceptionHandling().accessDeniedPage(<span class="string">&quot;/uuauth.html&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src= "/img/loading.gif" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210414150150.png" alt="9"></p>
</div><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">MoYu-zc</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="http://example.com/2021/05/04/Spring%20Security%20%20%E4%B8%8A/">http://example.com/2021/05/04/Spring Security  上/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="http://example.com" target="_blank">FATE</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/SpringBoot/">SpringBoot</a><a class="post-meta__tags" href="/tags/SpringSecurity/">SpringSecurity</a></div><div class="post_share"><div class="social-share" data-image="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210504135113.png" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css"><script src="https://cdn.jsdelivr.net/npm/social-share.js/dist/js/social-share.min.js" defer></script></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏<div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="/img/wx.png" target="_blank"><img class="post-qr-code-img" data-lazy-src="/img/wx.png" alt="wechat"/></a><div class="post-qr-code-desc">wechat</div></li><li class="reward-item"><a href="/img/zfb.jpg" target="_blank"><img class="post-qr-code-img" data-lazy-src="/img/zfb.jpg" alt="alipay"/></a><div class="post-qr-code-desc">alipay</div></li></ul></div></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2021/05/06/Spring%20Security%20%20%E4%B8%8B/"><img class="prev-cover" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210223164156.jpg" onerror="onerror=null;src='/img/404.jpg'"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Spring Security  下</div></div></a></div><div class="next-post pull-right"><a href="/2021/04/24/JWT/"><img class="next-cover" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210424222009.png" onerror="onerror=null;src='/img/404.jpg'"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">JWT</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span> 相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2021/05/06/Spring Security  下/" title="Spring Security  下"><img class="cover" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210223164156.jpg"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-05-06</div><div class="title">Spring Security  下</div></div></a></div><div><a href="/2021/04/12/Mybatis-plus 上/" title="Mybatis-plus 上"><img class="cover" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210319092322.jpg"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-04-12</div><div class="title">Mybatis-plus 上</div></div></a></div><div><a href="/2021/04/15/Mybatis-plus 下/" title="Mybatis-plus 下"><img class="cover" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210219145449.jpg"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-04-15</div><div class="title">Mybatis-plus 下</div></div></a></div><div><a href="/2021/05/26/Could not autowire. No beans of 'xxx' type found/" title="Could not autowire. No beans of 'xxx' type found"><img class="cover" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210223164156.jpg"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-05-26</div><div class="title">Could not autowire. No beans of 'xxx' type found</div></div></a></div><div><a href="/2021/03/22/07.Spring Boot合集/" title="Spring Boot合集"><img class="cover" data-lazy-src="https://gitee.com/MoYu-zc/picgo/raw/master/img/20210217161659.jpg"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-03-22</div><div class="title">Spring Boot合集</div></div></a></div></div></div><hr/><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="fas fa-comments fa-fw"></i><span> 评论</span></div></div><div class="comment-wrap"><div><div class="vcomment" id="vcomment"></div></div></div></div></article></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2022 By MoYu-zc</div><div class="footer_custom_text">Welcome,This is MoYu</div><div class="icp"><a target="_blank" rel="noopener" href="http://www.beian.gov.cn"><img class="icp-icon" src="/img/icp.png"/><span>豫ICP备20001029号</span></a></div></div></footer></div><section id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">繁</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog"></i></button><a id="to_comment" href="#post-comment" title="直达评论"><i class="fas fa-comments"></i></a><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="chat_btn" type="button" title="rightside.chat_btn"><i class="fas fa-sms"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></section><div class="search-dialog" id="local-search"><div class="search-dialog__title" id="local-search-title">本地搜索</div><div id="local-input-panel"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div></div><hr/><div id="local-search-results"><div id="local-hits"></div><div id="local-stats"><div class="local-search-stats__hr" id="hr"><span>由</span> <a target="_blank" rel="noopener" href="https://github.com/wzpan/hexo-generator-search" style="color:#49B1F5;">hexo-generator-search</a>
 <span>提供支持</span></div></div></div><span class="search-close-button"><i class="fas fa-times"></i></span></div><div id="search-mask"></div><div><script src="https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"></script><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"><script>if (document.getElementsByClassName('mermaid').length) {
  if (window.mermaidJsLoad) mermaid.init()
  else {
    $.getScript('https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js', function () {
      window.mermaidJsLoad = true
      mermaid.initialize({
        theme: 'default',
      })
      false && mermaid.init()
    })
  }
}</script><script>function loadValine () {
  function initValine () {
    const initData = {
      el: '#vcomment',
      appId: 'uANjuzIND1m5J2AmUxp5cG6K-gzGzoHsz',
      appKey: '4ViTsc6RFpqLm1PtCIlXh5m7',
      placeholder: '请留下你的疑问、评论，我会很快回复滴~~',
      avatar: 'monsterid',
      meta: 'nick,mail,link'.split(','),
      pageSize: '10',
      lang: 'zh-CN',
      recordIP: true,
      serverURLs: '',
      emojiCDN: '',
      emojiMaps: "",
      enableQQ: true,
      path: window.location.pathname,
    }

    if (true) { 
      initData.requiredFields= ('nick,mail'.split(','))
    }

    const valine = new Valine(initData)
  }

  if (typeof Valine === 'function') initValine() 
  else $.getScript('https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js', initValine)
}

if ('Valine' === 'Valine' || !true) {
  if (true) btf.loadComment(document.querySelector('#vcomment'),loadValine)
  else setTimeout(() => loadValine(), 0)
} else {
  function loadOtherComment () {
    loadValine()
  }
}</script><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div><script type="text/javascript" src="/js/fairyDustCursor.js"></script><script defer="defer" id="fluttering_ribbon" mobile="true" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js"></script><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/activate-power-mode.min.js"></script><script>POWERMODE.colorful = true;
POWERMODE.shake = false;
POWERMODE.mobile = true;
document.body.addEventListener('input', POWERMODE);
</script><script>window.$crisp = [];
window.CRISP_WEBSITE_ID = "86484e26-1f47-4402-a211-1efa3b0a704e";
(function () {
  d = document;
  s = d.createElement("script");
  s.src = "https://client.crisp.chat/l.js";
  s.async = 1;
  d.getElementsByTagName("head")[0].appendChild(s);
})();
$crisp.push(["safe", true])

if (true) {
  $crisp.push(["do", "chat:hide"])
  $crisp.push(["on", "chat:closed", function() {
    $crisp.push(["do", "chat:hide"])
  }])
  var chatBtnFn = () => {
    var chatBtn = document.getElementById("chat_btn")
    chatBtn.addEventListener("click", function(){
      $crisp.push(["do", "chat:show"])
      $crisp.push(["do", "chat:open"])

    });
  }
  chatBtnFn()
} else {
  if (true) {
    function chatBtnHide () {
      $crisp.push(["do", "chat:hide"])
    }
    function chatBtnShow () {
      $crisp.push(["do", "chat:show"])
    }
  }
}</script></div></body></html>